/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

const STATUS = { PENDING: 'pending', FULFILLED: 'fulfilled', REJECTED: 'rejected' }

class MyPromise {
  status = STATUS.PENDING
  value = undefined
  error = undefined
  fulfillCb = []
  rejectCb = []

	constructor(executor) {
    /**
     * executor 为MyPromise 类实例化时传入构造函数的 执行器，改执行器在实例化时会立即执行
     * executor 接收两个 参数，分别为 成功 和 失败 时的回调函数，通常以 resolve, reject 表示
     * 执行器描述了该 Promise 对象该如何进行，何时更新状态，执行哪个回调
     */
		try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }

  /**
   * 静态方法 all 的表现为:
   * 1. 接收一个promise对象的数组作为参数，实际all的数组参数中，只需满足拥有 Symbol.iterator 迭代器即可
   *    目前默认包含迭代器的类型有: Array, TypeArray, String, Map, Set
   * 2. 等待所有Promise执行完成后返回结果
   * 3. 按传入顺序返回执行结果的列表
   * 4. 结果被包裹在一个promise中返回
   * 5. Promise 中出现报错的表现为：立即返回报错，剩余项仍然执行，但不再返回结果
   */
  static all(list) {
    try {
      return new MyPromise((resolve, reject) => {
        const result = new Array(list.length)
        let done = 0
        list.forEach((el, i) => {
          if (el instanceof MyPromise) {
            el.then(v => result[i] = v)
              .catch(e => {
                result[i] = e
                reject(e)
              })
              .finally(_ => {
                done += 1
                if (done === list.length) resolve(result)
              })
          } else {
            done += 1
            result[i] = el
            if (done === list.length) resolve(result)
          }
        })
      })
    } catch (e) {
      return MyPromise.reject(e)
    }
  }
  /**
   * 静态方法 race 的表现为:
   * 1. 接收的参数类型和 all 方法保持一致
   * 2. 与 all 方法不同的是，race 方法在列表中出现第一个状态更新的项时即返回它，无论它成功或失败
   * 3. 即: race 方法返回的promise，状态和值与第一个更新状态的promise保持一致
   */
  static race(list) {
    try {
      return new MyPromise((resolve, reject) => {
        list.forEach(el => {
          if (el instanceof MyPromise) {
            el.then(resolve, reject)
          } else {
            resolve(el)
          }
        })
      })
    } catch (e) {
      return MyPromise.reject(e)
    }
  }

  /**
   * 静态方法 any 的表现为:
   * 1. 接收的参数类型和 all 方法保持一致
   * 2. 当其中的一个 promise 成功，就返回那个成功的promise的值
   * 3. 即: 在 race 基础上，忽略出现异常的项，只取第一个成功的项
   * 4. 如果所有传入的 promises 都失败, Promise.any 将返回异步失败，和一个 AggregateError 对象，它继承自 Error，有一个 error 属性，属性值是由所有失败值填充的数组。(FROM MDN)
   * 5. 从第四点可知，any 内部需维护一个存储 失败值 的数组，以便在所有项都失败时抛出
   */
  static any(list) {
    try {
      return new MyPromise((resolve, reject) => {
        const errors = []
        list.forEach(el => {
          if (el instanceof MyPromise) {
            el.then(resolve, e => errors.push(e))
              .finally(_ => {
                if (errors.length === list.length) {
                  // new AggregateError(errors, 'All Promises rejected')
                  // AggregateError 错误类型本地还未支持，暂用 Error 模拟一下
                  const err = new Error()
                  err.message = 'All Promises rejected'
                  err.name = 'AggregateError'
                  err.errors = errors
                  reject(err)
                }
              })
          } else {
            resolve(el)
          }
        })
      })
    } catch (e) {
      return MyPromise.reject(e)
    }
  }

  /**
   * 静态方法 allSettled 的表现为:
   * 1. 接收的参数类型和 all 方法保持一致
   * 2. 在所有项状态更新后，总是返回结果列表
   * 3. 将异常项的失败值放在对应顺序的列表位置上
   * 4. 该方法主要是为了弥补执行 all 方法的一个弊端: 当其中一项失败，则所有结果都无法获取，这在某些场景下并不是我们期待的结果
   */
  static allSettled(list) {
    try {
      return new MyPromise((resolve, reject) => {
        const result = new Array(list.length)
        let done = 0
        list.forEach((el, i) => {
          if (el instanceof MyPromise) {
            el.then(v => result[i] = v, e => result[i] = e)
              .finally(_ => {
                done += 1
                if (done === list.length) resolve(result)
              })
          } else {
            done += 1
            result[i] = el
            if (done === list.length) resolve(result)
          }
        })
      })
    } catch (e) {
      return MyPromise.reject(e)
    }
  }

  /**
   * resolve 和 reject 方法是更新 Promise 对象状态的方法
   * promise 对象特性之一是状态一旦确定为 fulfilled 或者 rejected，则无法再次改变
   * 因此在原型对象上的状态更新函数在执行时，需第一时间判断当前promise对象的状态是否已确定
   * 如果已确定，则不能继续执行
   * 如果未确定，则根据调用的状态更新函数，确定它的状态
   * 
   */
  // 静态的resolve方法目的是返回一个确定Promise状态为 fulfilled 的 promise 对象
  static resolve = value => new MyPromise(resolve => resolve(value))
  // 静态方法reject方法目的是返回一个确定Promise状态为 rejected 的 promise 对象
  static reject = error => new MyPromise((resolve, reject) => reject(error))

  resolve = v => {
    if (this.status !== STATUS.PENDING) return
    this.status = STATUS.FULFILLED
    this.value = v
    // then 方法执行时，可能存在promise的状态尚未改变，无法确定执行 成功 还是 失败的分支，因此会在 fulfillCb
    // 和 rejectCb 中分别存储每个步骤中注册的成功和失败函数，并在此时状态已确定时一一取出并执行
    while (this.fulfillCb.length) this.fulfillCb.shift()()
  }
  reject = e => {
    if (this.status !== STATUS.PENDING) return
    this.status = STATUS.REJECTED
    this.error = e
    while (this.rejectCb.length) this.rejectCb.shift()()
  }

  /**
   * then 方法表示 期待在promise状态改变后，得到成功或者失败的信号，并在其有
   * 返回值的情况下，获取这个值给到它的两个回调函数进行具体的操作
   * 因此:
   * 1. then 方法接收两个参数，分别为: 
   * resolve 时的回调 onFulfilled
   * reject 时的回调 onRejected
   * 2. then 方法执行时，有可能promise状态尚未改变，这时需将两个回调函数push进各自的待执行函数数组
   * 3. 两个回调函数可能存在返回值，如果存在，则需返回
   * 4. 因为then方法需可以进行链式调用，那么在每次执行完后，需返回一个promise对象
   * 5. 因此回调函数的返回值需通过 resolve 或者 reject 传给新的 promise
   * 6. 另一点是，如果返回值是一个promise对象，则应直接返回
   * 7. 直接返回promise会出现的问题是，如果返回值即当前promise，那么如果直接返回时
   *    会陷入循环调用的异常，因此在返回前，应先判断返回的promise对象是否和当前promise相等
   *    如果相等则直接抛出错误异常
   * 8. 要注意的是，当前promise在同步执行时无法立即获取，要获取当前promise，我们通过
   *    setimeout(_ => (), 0) 来包裹需要异步执行的代码即可
   *
   */
  then(onFulfilled, onRejected = MyPromise.reject){
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === STATUS.FULFILLED) {
        setTimeout(() => {
          try {
            const v = onFulfilled(this.value)
            promiseHandler(promise, v, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else if (this.status === STATUS.REJECTED) {
        setTimeout(() => {
          try {
            const e = onRejected(this.error)
            promiseHandler(promise, e, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else {
        this.fulfillCb.push(() => {
          setTimeout(() => {
            try {
              const v = onFulfilled(this.value)
              promiseHandler(promise, v, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.rejectCb.push(() => {
          setTimeout(() => {
            try {
              const e = onRejected(this.error)
              promiseHandler(promise, e, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
      }
    })
    // console.log(this)
    return promise
  }
  /**
   * 1. catch 方法其实是 then(undefined, onRejected) 的语法糖
   *    因此在实现上，我们直接返回 then(undefined, onRejected) 即可
   * 2. catch 方法在使用上的一个特点是，由于是链式调用，因此它的捕获异常会覆盖它之前整个链路
   *    通过 then 中传入异常捕获回调，只能捕获到then之前的异常，如果then内部的onFulfilled方法内部
   *    执行时出现异常，则无法被捕获到了，因此，通过catch链式捕获的写法，不仅更优雅，也更实用
   */
  catch(onRejected) {
    return this.then(undefined, onRejected)
  }
  /**
  * 1. finally 表示无论最终promise 状态变为成功或失败，最终都会执行
  * 2. finally 的回调不会获取到结果值，因为无法确认是否成功还是失败
  * 3. finally 在执行完成后,会返回一个promise
  *   1) 若finally的回调参数执行成功或者返回状态为fulfilled的promise对象，则 返回的promise值和状态和finally所属promise保持一致
  *   2) 若finally的回调参数执行失败，或者返回rejected的promise对象，则创建或返回该状态为失败的promise
  * 
  */
 finally(onFinally) {
    this.then(
      v => MyPromise.resolve(onFinally()).then(_ => v),
      e => MyPromise.resolve(onFinally()).then(_ => { throw e })
    )
 }
}

function promiseHandler (promise, value, resolve, reject) {
  if (promise === value) return reject('ERROR')
  if (value instanceof MyPromise) {
    return value.then(resolve, reject)
  } else {
    return resolve(value)
  }
}



const log = type => v => console.log(type, ': ', v)

// const p = new MyPromise((resolve, reject) => {
//   console.log('start')
//   setTimeout(() => {
//     resolve('success')
//   }, 2000)
//   // resolve('success')
// })

// p
//   .then(v => {
//     console.log('in then:', v)
//     return 200
//   })
//   .then(v => {
//     console.log(v)
//     throw new Error('test catch')
//   })
//   .then(v => console.log(v))
//   .catch(e => {
//     console.log('error', e)
//   })

// 测试静态方法 resovle & reject
// const p = MyPromise.resolve('test static resolve')
// const p = MyPromise.reject('test static reject')
// p.then(v => console.log(v), e => console.log(e))
// const aa = Promise.resolve('aa')
const createSuc = v => {
  return new MyPromise((resolve) => {
    setTimeout(_ => resolve(v), v * 1000)
  })
}
const createErr = v => MyPromise.reject(v)
// createErr(2).catch(er => console.log('errrr: ', er))
// const c = new Promise((_, rej) => {
//   console.log('hhe')
//   setTimeout(_ => rej('c'), 10000)
// })

const testarr = [
  createSuc(5),
  createSuc(3),
  createSuc(2),
  createErr(4)
]
// const a = Promise.all(testarr)
// MyPromise.resolve(1).then(v => MyPromise.resolve(222)).finally(v => console.log(v)).finally(e => console.log(g, 'g')).catch(console.log)
MyPromise.allSettled(testarr).then(log('succ')).catch(log('err'))